package com.demo.webflux.webclient;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;

import com.alibaba.dubbo.demo.Student;
import com.demo.webflux.domain.PasswordDto;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class Test {

	private static final Logger logger = LoggerFactory.getLogger(Test.class);

	public static void main(String[] args) throws InterruptedException {
		WebClient webClient = WebClient.builder()
									.baseUrl("http://localhost:8085")
									.filter(logRequest()) // 加过滤器
									.defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
									.defaultHeader(HttpHeaders.ACCEPT, "application/json")
									.build();
		//test1(webClient);
		//test2(webClient);
		//test3(webClient);
		test7(webClient);
		Thread.sleep(20000000);
	}

	// 对请求做详细的日志记录
	private static ExchangeFilterFunction logRequest() {
		return (clientRequest, next) -> {
			logger.info("Request: {} {}", clientRequest.method(), clientRequest.url());
			clientRequest.headers().forEach((name, values) -> values.forEach(value -> logger.info("{} = {}", name, value)));
			return next.exchange(clientRequest);
		};
	}

	private static void test1(WebClient webClient) {
		Flux<Student> flux = webClient.get()
					.uri("/sen/{owner}/{age}", "test", 20)
					// retrieve方法是获取响应主体并对其进行解码的最简单的方法
					.retrieve()
					.bodyToFlux(Student.class);
		flux.subscribe(s -> {
			System.out.println(s);
		});
	}

	private static void test2(WebClient webClient) {
		Flux<Student> flux = webClient.get()
					.uri("/sen/{owner}/{age}", "test", 20)
					// 如果希望对响应有更多的控制权，就需要使用exchange方法，该返回结果是一个ClientResponse对象，通过它可以获得响应的状态码、Cookie
					.exchange()
					.flatMapMany(res -> res.bodyToFlux(Student.class));
		flux.subscribe(s -> {
			System.out.println(s);
		});
	}

	private static void test3(WebClient webClient) {
		List<Student> students = new ArrayList<>();
		students.add(new Student("zsl", 123));

		Flux<Student> flux = webClient.post()
				.uri("/sen")
				// 如果有一个Mono或Flux类型的请求体，可以使用body方法来进行编码
				.body(Flux.fromIterable(students), Student.class)
				.retrieve()
				.bodyToFlux(Student.class);
		flux.subscribe(s -> {
			System.out.println(s);
		});
	}

	private static void test4(WebClient webClient) {
		MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
		map.add("userName", "zsl");
		map.add("userName", "zsl");

		Mono<Student> flux = webClient.post()
				.uri("/sen")
				// 如果要进行表单提交，可以使用MultiValueMap
				// syncBody可以直接接收对象，不需要转换为Publish
				.syncBody(map)
				.retrieve()
				.bodyToMono(Student.class);
		flux.subscribe(s -> {
			System.out.println(s);
		});
	}

	private static void test5(WebClient webClient) {
		MultipartBodyBuilder builder = new MultipartBodyBuilder();
		builder.part("fieldPart", "fieldValue");
		builder.part("filePart", new FileSystemResource("logo.png"));

		Mono<Student> flux = webClient.post()
				.uri("/sen")
				// 如果要进行文件提交，可以使用MultipartBodyBuilder
				.syncBody(builder.build())
				.retrieve()
				.bodyToMono(Student.class);
		flux.subscribe(s -> {
			System.out.println(s);
		});
	}

	private static void test6(WebClient webClient) {
		Flux<Student> flux = webClient.get()
				.uri("/sen/{owner}/{age}", "test", 20)
				.retrieve()
				// 当响应的状态码为4xx或5xx时，WebClient就会抛出一个WebClientResponseException异常，我们可以用onStatus方法来自定义对异常的处理方式。
				// 需要注意，WebClientResponseException只适用于使用retrieve方法进行远程请求的场景，exchange方法在获取4xx或5xx响应的情况下不会引发异常。因此当使用exchange方法时，我们需要自行检查状态码并以合适的方式处理它们
				.onStatus(HttpStatus::is4xxClientError, clientResponse ->
					Mono.error(new Exception())
				)
				.onStatus(HttpStatus::is5xxServerError, clientResponse ->
					Mono.error(new Exception())
				)
				.bodyToFlux(Student.class);
		flux.subscribe(s -> {
			System.out.println(s);
		});
	}

	private static void test7(WebClient webClient) {
		Mono<Void> monoVoid = webClient.post()
				.uri("/check")
				// 使用body方法来发送消息体，并准备使用BodyInserters.fromPublisher工厂方法来插入它
				.body(BodyInserters.fromPublisher(Mono.just(new PasswordDto("ack", "utf-8")), PasswordDto.class))
				.exchange()
				.flatMap(response -> {
					if(response.statusCode().is2xxSuccessful()) {
						return Mono.empty();
					}else if(response.statusCode() == HttpStatus.EXPECTATION_FAILED) {
						return Mono.error(new RuntimeException(""));
					}
					return Mono.error(new RuntimeException(""));
				});
		monoVoid.subscribe(s -> {
			System.out.println(s);
		});
	}

}
